package com.baomidou.wechat.core;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.wechat.common.aes.AesException;
import com.baomidou.wechat.common.aes.SHA1;
import com.baomidou.wechat.common.aes.WXBizMsgCrypt;
import com.baomidou.wechat.common.enums.EventType;
import com.baomidou.wechat.common.enums.MessageType;
import com.baomidou.wechat.common.util.StreamUtil;
import com.baomidou.wechat.exception.WechatException;
import com.baomidou.wechat.exception.WechatRunTimeException;
import com.baomidou.wechat.vo.MPAccount;
import com.baomidou.wechat.vo.event.BasicEvent;
import com.baomidou.wechat.vo.event.LocationEvent;
import com.baomidou.wechat.vo.event.MenuEvent;
import com.baomidou.wechat.vo.event.ScanCodeEvent;
import com.baomidou.wechat.vo.event.ScanEvent;
import com.baomidou.wechat.vo.event.SendLocationInfoEvent;
import com.baomidou.wechat.vo.event.SendPhotosEvent;
import com.baomidou.wechat.vo.message.BasicMsg;
import com.baomidou.wechat.vo.message.ImageMsg;
import com.baomidou.wechat.vo.message.LinkMsg;
import com.baomidou.wechat.vo.message.LocationMsg;
import com.baomidou.wechat.vo.message.MusicMsg;
import com.baomidou.wechat.vo.message.NewsMsg;
import com.baomidou.wechat.vo.message.TextMsg;
import com.baomidou.wechat.vo.message.VideoMsg;
import com.baomidou.wechat.vo.message.VoiceMsg;
import com.baomidou.wechat.vo.push.SentAllJobEvent;
import com.baomidou.wechat.vo.push.SentTmlJobEvent;

/**
 * WeChat-sdk 引导程序启动
 */
public class WechatBootstrap {

	private static final Logger logger = LoggerFactory.getLogger(XmlMsgBuilder.class);

	/* XML解析准备 */
	private SAXParser xmlParser;

	/* HTTP请求响应 */
	private HttpServletRequest request;
	private HttpServletResponse response;

	/* 消息处理器 */
	private WechatHandler handler;

	/* 公众号信息 */
	private MPAccount mpAct;

	protected WechatBootstrap() {
		try {
			xmlParser = SAXParserFactory.newInstance().newSAXParser();
		} catch (Exception e) {
			throw new WechatRunTimeException("初始化SAXParserFactory出现异常", e);
		}
	}

	public WechatBootstrap(HttpServletRequest request, HttpServletResponse response) {
		this();
		this.request = request;
		this.response = response;
	}

	public WechatBootstrap(HttpServletRequest request, HttpServletResponse response, MPAccount mpAct,
			WechatHandler handler) {
		this();
		this.request = request;
		this.response = response;
		this.mpAct = mpAct;
		this.handler = handler;
	}

	/**
	 * 设置公众号信息
	 * 
	 * @param mpAct
	 *            公众号信息
	 * @param WechatBootstrap
	 *            当前对象
	 */
	public WechatBootstrap setMpAct(MPAccount mpAct) {
		this.mpAct = mpAct;
		return this;
	}

	/**
	 * 设置微信消息处理器
	 * 
	 * @param handler
	 *            消息处理器
	 * @param WechatBootstrap
	 *            当前对象
	 */
	public WechatBootstrap setWechatHandler(WechatHandler handler) {
		this.handler = handler;
		return this;
	}

	/**
	 * 启动微信服务器
	 * 
	 * @param request
	 *            微信服务器请求
	 * @param response
	 *            响应微信服务器
	 * @throws IOException
	 */
	public void startup() throws IOException {
		String respmsg = "success";
		try {
			if ("GET".equals(request.getMethod())) {
				/* 开发者验证 */
				respmsg = check();
			} else {
				respmsg = interact();
			}
		} catch (Exception e) {
			logger.error(" wechat-sdk 启动异常：{}", e);
		}

		/* 输出回复消息 */
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html");
		response.getWriter().print(respmsg);
	}

	/**
	 * 微信成为开发者验证
	 * 
	 * @return 微信服务器随机字符串
	 */
	protected String check() {
		String signature = request.getParameter("signature"); /* 微信加密签名 */
		String timestamp = request.getParameter("timestamp"); /* 时间戳 */
		String nonce = request.getParameter("nonce"); /* 随机数 */
		String respmsg = "error";
		if (StringUtils.isNotBlank(signature) && StringUtils.isNotBlank(timestamp) && StringUtils.isNotBlank(nonce)) {
			try {
				String sha1 = SHA1.getSHA1(mpAct.getToken(), timestamp, nonce, "");
				if (StringUtils.equals(sha1, signature)) {
					/*
					 * 确认此次GET请求来自微信服务器，原样返回echostr参数内容， 则接入生效，成为开发者成功，否则接入失败
					 */
					respmsg = request.getParameter("echostr");
				}
			} catch (AesException e) {
				logger.error("验证开发者错误，请检查 token 是否正确 token={} , 异常：{}", mpAct.getToken(), e);
			}
		}
		return respmsg;
	}

	/**
	 * 与微信服务器交互处理过程
	 * 
	 * @param is
	 *            微信服务器推送消息
	 * @return 响应消息
	 */
	// TODO 是否考虑添加重复消息过滤功能
	protected String interact() throws Exception {
		InputStream is = request.getInputStream();
		String encrypt = request.getParameter("encrypt_type");
		WXBizMsgCrypt pc = null;
		String respmsg = "success";
		MessageHandler messageHandler = new MessageHandler();
		if (encrypt != null && "aes".equals(encrypt)) {
			/**
			 * 密文模式 
			 */
			try {
				pc = new WXBizMsgCrypt(mpAct.getToken(), mpAct.getAESKey(), mpAct.getAppId());

				String ts = request.getParameter("timestamp");
				String nonce = request.getParameter("nonce");
				String msgsign = request.getParameter("msg_signature");

				String decmsg = pc.decryptMsg(msgsign, ts, nonce, is.toString());
				xmlParser.parse(StreamUtil.toStream(decmsg), messageHandler);
				respmsg = pc.encryptMsg(responseXML(handleMsg(messageHandler)), ts, nonce);
			} catch (Exception e) {
				throw new WechatException("使用密文模式出现异常", e);
			}
		} else {
			/**
			 * 明文模式
			 */
			try {
				xmlParser.parse(is, messageHandler);
			} catch (Exception e) {
				throw new WechatException("明文模式下解析消息出现异常", e);
			}
			respmsg = responseXML(handleMsg(messageHandler));
		}

		return respmsg;
	}

	/**
	 * 微信消息处理
	 * 
	 * @return 回复消息
	 */
	protected BasicMsg handleMsg(MessageHandler messageHandler) {
		String msgtype = messageHandler.getValues().get("msgType");
		if ("event".equals(msgtype)) {
			return handleEventMsg(messageHandler);
		} else {
			return handleNormalMsg(messageHandler);
		}
	}

	/**
	 * 处理普通消息
	 * 
	 * @return 回复消息
	 */
	protected BasicMsg handleNormalMsg(MessageHandler messageHandler) {
		Map<String, String> values = messageHandler.getValues();
		MessageType mt = MessageType.def;
		try {
			mt = MessageType.valueOf(values.get("msgType"));
		} catch (Exception e) {
			logger.error("There are have found new meessage type in wechat.");
		}
		
		BasicMsg msg = null;
		switch (mt) {
		case text:
			msg = handler.text(new TextMsg(values));
			break;
		case image:
			msg = handler.image(new ImageMsg(values));
			break;
		case voice:
			msg = handler.voice(new VoiceMsg(values));
			break;
		case video:
			msg = handler.video(new VideoMsg(values));
			break;
		case shortvideo:
			msg = handler.shortVideo(new VideoMsg(values));
			break;
		case location:
			msg = handler.location(new LocationMsg(values));
			break;
		case link:
			msg = handler.link(new LinkMsg(values));
			break;
		default:
			msg = handler.defMsg(new BasicMsg(values));
			break;
		}
		return msg;
	}

	/**
	 * 处理事件消息
	 * 
	 * @return 回复消息
	 */
	protected BasicMsg handleEventMsg(MessageHandler messageHandler) {
		Map<String, String> values = messageHandler.getValues();
		EventType et = EventType.def;
		try {
			et = EventType.valueOf(values.get("event"));
		} catch (Exception e) {
			logger.error("There are have found new event type from wechat.");
		}
		
		BasicMsg msg = null;
		switch (et) {
		case subscribe:
			msg = handler.eSub(new BasicEvent(values));
			break;
		case unsubscribe:
			handler.eUnSub(new BasicEvent(values));
			break;
		case scan:
			msg = handler.eScan(new ScanEvent(values));
			break;
		case location:
			handler.eLocation(new LocationEvent(values));
			break;
		case click:
			msg = handler.eClick(new MenuEvent(values));
			break;
		case view:
			handler.eView(new MenuEvent(values));
			break;
		case scancode_push:
			msg = handler.eScanCodePush(new ScanCodeEvent(values));
			break;
		case scancode_waitmsg:
			msg = handler.eScanCodeWait(new ScanCodeEvent(values));
			break;
		case pic_sysphoto:
			msg = handler.ePicSysPhoto(new SendPhotosEvent(values));
			break;
		case pic_photo_or_album:
			msg = handler.ePicPhotoOrAlbum(new SendPhotosEvent(values));
			break;
		case pic_weixin:
			msg = handler.ePicWeixin(new SendPhotosEvent(values));
			break;
		case location_select:
			msg = handler.eLocationSelect(new SendLocationInfoEvent(values));
			break;
		// TODO 暂不清楚微信的推送
		/*
		 * case media_id: case view_limited: BasicEvent mvbe = new
		 * BasicEvent(msgHandler.getValues()); msg = handler.defEvent(mvbe);
		 * break;
		 */
		case templatesendjobfinish:
			handler.eSentTmplJobFinish(new SentTmlJobEvent(values));
			break;
		case masssendjobfinish:
			handler.eSentAllJobFinish(new SentAllJobEvent(values));
			break;
		default:
			msg = handler.defEvent(new BasicEvent(values));
			break;
		}
		return msg;
	}

	/**
	 * 输出回复消息
	 * 
	 * @param msg
	 *            回复消息数据
	 * @return XML消息
	 */
	protected String responseXML(BasicMsg msg) {
		String respmsg = "success";
		if (msg == null || StringUtils.isBlank(msg.getMsgType())) {
			return respmsg;
		}

		/* 交换 fromUser 和 toUser */
		String fromUser = msg.getFromUserName();
		String toUser = msg.getToUserName();
		msg.setFromUserName(toUser);
		msg.setToUserName(fromUser);
		MessageType mt = MessageType.valueOf(msg.getMsgType());
		switch (mt) {
		case text:
			respmsg = XmlMsgBuilder.create().text((TextMsg) msg).build();
			break;
		case image:
			respmsg = XmlMsgBuilder.create().image((ImageMsg) msg).build();
			break;
		case voice:
			respmsg = XmlMsgBuilder.create().voice((VoiceMsg) msg).build();
			break;
		case music:
			respmsg = XmlMsgBuilder.create().music((MusicMsg) msg).build();
			break;
		case video:
			respmsg = XmlMsgBuilder.create().video((VideoMsg) msg).build();
			break;
		case news:
			respmsg = XmlMsgBuilder.create().news((NewsMsg) msg).build();
			break;
		default:
			break;
		}
		return respmsg;
	}

}
